/*
 * Copyright (C) 2021 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


#pragma once

#include "fake-pipeline2/Base.h"
#include "EmulatedFakeRotatingCameraDevice.h"

#include <ui/GraphicBufferAllocator.h>
#include <ui/GraphicBufferMapper.h>
#include <utils/Mutex.h>
#include <utils/Thread.h>
#include <utils/Timers.h>

namespace android {

class CameraRotator : private Thread, public virtual RefBase {
public:
    CameraRotator(int w, int h);
    ~CameraRotator();


    status_t startUp();
    status_t shutDown();


    void setExposureTime(uint64_t ns);
    void setFrameDuration(uint64_t ns);
    void setSensitivity(uint32_t gain);

    /*
     * Each Buffer in "buffers" must be at least stride*height*2 bytes in size.
     */
    void setDestinationBuffers(Buffers *buffers);
    /*
     * To simplify tracking the sensor's current frame.
     */
    void setFrameNumber(uint32_t frameNumber);

    /*
     * Synchronizing with sensor operation (vertical sync).
     */

    /*
     * Wait until the sensor outputs its next vertical sync signal, meaning it
     * is starting readout of its latest frame of data.
     *
     * Returns:
     *     true if vertical sync is signaled; false if the wait timed out.
     */
    bool waitForVSync(nsecs_t reltime);

    /*
     * Wait until a new frame has been read out, and then return the time
     * capture started. May return immediately if a new frame has been pushed
     * since the last wait for a new frame.
     *
     * Returns:
     *     true if new frame is returned; false if timed out.
     */
    bool waitForNewFrame(nsecs_t reltime, nsecs_t *captureTime);

    /*
     * Interrupt event servicing from the sensor. Only triggers for sensor
     * cycles that have valid buffers to write to.
     */
    struct CameraRotatorListener {
        enum Event {
            EXPOSURE_START,
        };

        virtual void onCameraRotatorEvent(uint32_t frameNumber, Event e,
                nsecs_t timestamp) = 0;
        virtual ~CameraRotatorListener();
    };

    void setCameraRotatorListener(CameraRotatorListener *listener);

    /*
     * Static Sensor Characteristics
     */
    const uint32_t mWidth, mHeight;
    const uint32_t mActiveArray[4];

    static const nsecs_t kExposureTimeRange[2];
    static const nsecs_t kFrameDurationRange[2];
    static const nsecs_t kMinVerticalBlank;

    static const int32_t kSensitivityRange[2];
    static const uint32_t kDefaultSensitivity;

    static const char kHostCameraVerString[];

  private:
    int32_t mLastRequestWidth, mLastRequestHeight;

    /*
     * Defines possible states of the emulated camera device object.
     */
    enum EmulatedCameraDeviceState {
        // Object has been constructed.
        ECDS_CONSTRUCTED,
        // Object has been initialized.
        ECDS_INITIALIZED,
        // Object has been connected to the physical device.
        ECDS_CONNECTED,
        // Camera device has been started.
        ECDS_STARTED,
    };
    // Object state.
    EmulatedCameraDeviceState mState;

    const char *mDeviceName;
    GraphicBufferAllocator* mGBA;
    GraphicBufferMapper*    mGBM;

    // Always lock before accessing control parameters.
    Mutex mControlMutex;
    /*
     * Control Parameters
     */
    Condition mVSync;
    bool mGotVSync;
    uint64_t mFrameDuration;
    Buffers *mNextBuffers;
    uint32_t mFrameNumber;

    // Always lock before accessing readout variables.
    Mutex mReadoutMutex;
    /*
     * Readout Variables
     */
    Condition mReadoutAvailable;
    Condition mReadoutComplete;
    Buffers *mCapturedBuffers;
    nsecs_t mCaptureTime;
    CameraRotatorListener *mListener;

    // Time of sensor startup (used for simulation zero-time point).
    nsecs_t mStartupTime;
    int32_t mHostCameraVer;
    bool mIsMinigbm;

  private:
    /*
     * Inherited Thread Virtual Overrides
     */
    virtual status_t readyToRun() override;
    /*
     * CameraRotator capture operation main loop.
     */
    virtual bool threadLoop() override;

    /*
     * Members only used by the processing thread.
     */
    nsecs_t mNextCaptureTime;
    Buffers *mNextCapturedBuffers;

    void captureRGBA(uint32_t width, uint32_t height, uint32_t stride,
                     int64_t *timestamp, buffer_handle_t* handle);
    void captureYU12(uint32_t width, uint32_t height, uint32_t stride,
                     int64_t *timestamp, buffer_handle_t* handle);
    void captureRGBA(uint8_t *img, uint32_t width, uint32_t height,
                     uint32_t stride, int64_t *timestamp);
    void captureYU12(uint8_t *img, uint32_t width, uint32_t height,
                     uint32_t stride, int64_t *timestamp);
    void captureRGB(uint8_t *img, uint32_t width, uint32_t height,
                    uint32_t stride, int64_t *timestamp);

private:

    EmulatedFakeRotatingCameraDevice    mRender;

    status_t queryStart(uint32_t pixel_format, int width, int height);

    status_t queryStart();

    status_t queryStop();

    status_t queryFrame(void* vframe,
                        void* pframe,
                        size_t vframe_size,
                        size_t pframe_size,
                        float r_scale,
                        float g_scale,
                        float b_scale,
                        float exposure_comp,
                        int64_t* frame_time);


    status_t queryFrame(int wdith,
                        int height,
                        uint32_t pixel_format,
                        uint64_t offset,
                        float r_scale,
                        float g_scale,
                        float b_scale,
                        float exposure_comp,
                        int64_t* frame_time);

};

};  // end of namespace android
